/**
 * 客户端函数模型
 *
 * Created by snowingsea on 2020/03/06.
 */
const querystring = require('querystring');
const request = require('request-promise');
const TestCase = require('./test-case');
const TestCaseGroup = require('./test-case-group');
const testerURI = require('./../api/tester/uri');
const { fixedAddrBasesAssignmentKind } = require('../../phoenix/models/functions');

class FunctionModel {
  constructor(rootDirectory, fileId, functionId, functionName) {
    this.rootDirectory = rootDirectory;
    this.fileId = fileId;
    this.functionId = functionId;
    this.functionName = functionName;
  }

  /**
   * 添加指针目标变量
   * @param {String} variableName 变量名
   * @param {String} typeName 类型名
   * @param {String} elementTypeName 数组单元素类型名
   * @param {String} length 长度
   * @return {Promise}
   */
  addMallocVariable(variableName, { typeName = null, elementTypeName = null, length = null }) {
    return new Promise((resolve, reject) => {
      this.rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          const { accessToken } = this.rootDirectory.projectVersion.project.creator.token;
          const form = {
            variableName,
            typeName,
            elementTypeName,
            length,
          };
          const url = `${testerURI}/functions/${this.functionId}/malloc.json`;
          return request.post({
            url,
            headers: { Authorization: `Bearer ${accessToken}` },
            form,
            json: true,
          });
        })
        .then(() => this.rootDirectory.projectVersion.project.creator.logout())
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 添加桩函数
   * @param {String} stubName 桩函数名
   * @param {String} returnType 返回值类型
   * @param {List} paramTypeNames 参数类型列表
   * @param {List} paramNames 参数名称列表
   * @param {Number} times 调用次数
   * @return {Promise}
   */
  addStub(
    {
      stubName = null, returnType = null, paramTypeNames = null, paramNames = null, times = null,
    },
  ) {
    return new Promise((resolve, reject) => {
      this.rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          const { accessToken } = this.rootDirectory.projectVersion.project.creator.token;
          const query = {
            stubName,
            returnType,
            paramTypeNames,
            paramNames,
            times,
          };
          const url = `${testerURI}/functions/${this.functionId}/stubs.json?${querystring.stringify(query)}`;
          return request.post({
            url,
            headers: { Authorization: `Bearer ${accessToken}` },
          });
        })
        .then(() => this.rootDirectory.projectVersion.project.creator.logout())
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 删除桩函数
   * @param {String} stubName
   * @return {Promise}
   */
  deleteStub(stubName) {
    return new Promise((resolve, reject) => {
      this.rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          const { accessToken } = this.rootDirectory.projectVersion.project.creator.token;
          const url = `${testerURI}/functions/${this.functionId}/stubs/${stubName}.json}`;
          return request.delete({
            url,
            headers: { Authorization: `Bearer ${accessToken}` },
          });
        })
        .then(() => this.rootDirectory.projectVersion.project.creator.logout())
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 获取函数变量数据
   * @param {String} accessToken 访问token
   * @return {Promise}
   */
  getVariables(accessToken = undefined) {
    return new Promise((resolve, reject) => {
      let variables;
      this.rootDirectory.projectVersion.project.creator.token.accessToken = accessToken;
      Promise.resolve()
        .then(() => {
          if (!accessToken) {
            return this.rootDirectory.projectVersion.project.creator.login();
          }
          return Promise.resolve();
        })
        .then(() => {
          const { accessToken } = this.rootDirectory.projectVersion.project.creator.token;
          const url = `${testerURI}/functions/${this.functionId}/variables.json`;
          return request.get({
            url,
            headers: { Authorization: `Bearer ${accessToken}` },
            json: true,
          });
        })
        .then((res) => {
          variables = res;
          if (!accessToken) {
            return this.rootDirectory.projectVersion.project.creator.logout();
          }
          return Promise.resolve();
        })
        .then(() => {
          resolve(variables);
        })
        .catch(reject);
    });
  }

  /**
   * 获取测试用例列表
   * @return {Promise}
   */
  getTests() {
    return new Promise((resolve, reject) => {
      const tests = [];
      this.rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          const { accessToken } = this.rootDirectory.projectVersion.project.creator.token;
          const url = `${testerURI}/functions/${this.functionId}/tests.json`;
          return request.get({
            url,
            headers: { Authorization: `Bearer ${accessToken}` },
            json: true,
          });
        })
        .then((res) => {
          res.tests.forEach(({ testId, status, kind }) => {
            tests.push(new TestCase(this, this.functionId, testId, status, kind));
          });
          return this.rootDirectory.projectVersion.project.creator.logout();
        })
        .then(() => {
          resolve(tests);
        })
        .catch(reject);
    });
  }

  /**
   * 添加绝对地址
   * @param {String} fixedAddressName 绝对地址名
   * @param {String} length 字节长度
   * @param {String} pointerTargets 绝对地址指针目标
   * @return {Promise<unknown>}
   */
  addFixedAddressVariable(fixedAddressName, length = null, pointerTargets = null, assignmentKind = fixedAddrBasesAssignmentKind.baseOffset) {
    return new Promise((resolve, reject) => {
      this.rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          const { accessToken } = this.rootDirectory.projectVersion.project.creator.token;
          const form = {
            fixedAddressName,
            pointerTargets,
            length,
            assignmentKind,
          };
          const options = {
            url: `${testerURI}/functions/${this.functionId}/fixed-addresses.json`,
            headers: { Authorization: `Bearer ${accessToken}` },
            form,
            json: true,
          };
          return request.post(options);
        })
        .then(() => this.rootDirectory.projectVersion.project.creator.logout())
        .then(resolve)
        .catch(reject);
    });
  }

  /**
   * 添加编组
   * @return {Promise}
   */
  addTestCaseGroup() {
    return new Promise((resolve, reject) => {
      let group;
      this.rootDirectory.projectVersion.project.creator.login()
        .then(() => {
          const { accessToken } = this.rootDirectory.projectVersion.project.creator.token;
          const form = {
            parentId: null,
            description: '13579',
          };
          const options = {
            url: `${testerURI}/functions/${this.functionId}/testcase-groups.json`,
            headers: { Authorization: `Bearer ${accessToken}` },
            form,
            json: true,
          };
          return request.post(options);
        })
        .then((res) => {
          const kind = res.result ? res.result.kind : undefined;
          group = new TestCaseGroup(this, this.functionId, res.groupId, kind);
          return this.rootDirectory.projectVersion.project.creator.logout();
        })
        .then(() => resolve(group))
        .catch(reject);
    });
  }
}

/**
 * Export model definition object.
 */
module.exports = FunctionModel;
